package com.ls.mq.actor.impl;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

import com.ls.mq.exception.MQException;
import com.ls.mq.iterface.IMQCallback;
import com.ls.mq.iterface.IMQFuture;


public class DefaultMQFuture implements IMQFuture {
	private final long timeoutInNanos;
	private final long startTimeInNanos;
	private final Sync sync;
	private IMQCallback callback;

	public DefaultMQFuture(long timeout, TimeUnit unit) {
		this.timeoutInNanos = unit.toNanos(timeout);
		this.startTimeInNanos = System.nanoTime();
		this.sync = new Sync();
	}

	public DefaultMQFuture(long timeoutInNanos) {
		this.timeoutInNanos = timeoutInNanos;
		this.startTimeInNanos = System.nanoTime();
		this.sync = new Sync();
	}

	public long timeoutInNanos() {
		return this.timeoutInNanos;
	}

	public boolean isExpired() {
		return ((this.timeoutInNanos != 0L) && (this.timeoutInNanos
				- (System.nanoTime() - this.startTimeInNanos) <= 0L));
	}

	public void await() throws InterruptedException {
		if (this.timeoutInNanos == 0L) {
			this.sync.acquireSharedInterruptibly(0);
			return;
		}
		long waitTime = this.timeoutInNanos
				- (System.nanoTime() - this.startTimeInNanos);
		if (waitTime < 0L)
			return;
		this.sync.tryAcquireSharedNanos(0, waitTime);
	}

	public void awaitBlocking() {
		if (this.timeoutInNanos == 0L) {
			this.sync.acquireShared(0);
			return;
		}
		while (true)
			try {
				long waitTime = this.timeoutInNanos
						- (System.nanoTime() - this.startTimeInNanos);
				if (waitTime < 0L)
					return;
				this.sync.tryAcquireSharedNanos(0, waitTime);
				return;
			} catch (InterruptedException localInterruptedException) {
			}
	}

	public Object result() {
		return this.sync.result;
	}

	public Throwable exception() {
		return this.sync.exception;
	}

	public synchronized void setCallback(IMQCallback callback) {
		this.callback = callback;
		if (this.sync.innerIsDone())
			if (this.sync.exception != null)
				callback.onException(this.sync.exception);
			else
				callback.onComplete(this.sync.result);
	}

	public boolean isCompleted() {
		return this.sync.innerIsDone();
	}

	public synchronized void completeWithResult(Object result) {
		if (this.sync.innerIsDone()) {
			throw new IllegalStateException("done");
		}
		set(result);
		if (this.callback != null)
			this.callback.onComplete(result);
	}

	public synchronized void completeWithException(Throwable exception) {
		if (this.sync.innerIsDone()) {
			throw new IllegalStateException("done");
		}
		setException(exception);
		if (this.callback != null)
			this.callback.onException(exception);
	}

	public void set(Object v) {
		if(v!=null&&v instanceof Throwable){
			setException((Throwable) v);
			return;
		}
		this.sync.innerSet(v);
	}

	public void setException(Throwable t) {
		this.sync.innerSetException(t);
	}

	private final static class Sync extends AbstractQueuedSynchronizer {
		/**
		 * 
		 */
		private static final long serialVersionUID = -424065125960732701L;
		private Object result;
		private Throwable exception;

		private boolean finished(int state) {
			return ((state & 0x1) != 0);
		}

		protected int tryAcquireShared(int ignore) {
			return ((innerIsDone()) ? 1 : -1);
		}

		protected boolean tryReleaseShared(int ignore) {
			return true;
		}

		boolean innerIsDone() {
			return finished(getState());
		}

		void innerSet(Object v) {
			int s;
			do {
				s = getState();
				if (finished(s))
					return;
			} while (!(compareAndSetState(s, 1)));

			this.result = v;
			releaseShared(0);
		}

		void innerSetException(Throwable t) {
			int s;
			do {
				s = getState();
				if (finished(s))
					return;
			} while (!(compareAndSetState(s, 1)));

			this.exception = t;
			this.result = null;
			releaseShared(0);
		}
	}

	@Override
	public Object get() {
		awaitBlocking();
		if (isCompleted()) {
			if (exception() != null) {
				if (exception() instanceof RuntimeException) {
					throw ((RuntimeException) exception());
				}
				throw new MQException(exception());
			}
			return result();
		}
		throw new MQException("task not finished ,overtime");
	}
}
